import { getAppMode } from "store/selectors";
import { useApiClientFeatureContext, useApiClientRepository } from "../../contexts/meta";
import { useSelector } from "react-redux";
import { useApiClientContext } from "../../contexts";
import { AutogenerateStoreContext } from "../../store/autogenerateContextProvider";
import { useCallback, useContext, useMemo } from "react";
import { HttpRequestPreparationService } from "../../helpers/httpRequestExecutor/httpRequestPreparationService";
import { HttpRequestValidationService } from "../../helpers/httpRequestExecutor/httpRequestValidationService";
import { renderVariables } from "backend/environment/utils";
import { useAPIEnvironment } from "../../store/apiRecords/ApiRecordsContextProvider";
import { useCommand } from "../../commands";
import { setRuntimeStore } from "../../store/runtimeVariables/utils";
import { toast } from "utils/Toast";
import { ApiClientFeatureContext } from "features/apiClient/store/apiClientFeatureContext/apiClientFeatureContext.store";
import { APIClientWorkloadManager } from "features/apiClient/helpers/modules/scriptsV2/workloadManager/APIClientWorkloadManager";
import { BaseExecutionContext } from "features/apiClient/helpers/httpRequestExecutor/scriptExecutionContext";

type ExecutorConstructor<T> = new (
  ctx: ApiClientFeatureContext,
  requestPreparer: HttpRequestPreparationService,
  requestValidator: HttpRequestValidationService,
  workloadManager: APIClientWorkloadManager,
  handleUpdatesFromExecutionWorker: (state: any) => Promise<void>,
  appMode: "EXTENSION" | "DESKTOP"
) => T;

export const useRequestExecutorFactory = <T>(
  ExecutorClass: ExecutorConstructor<T>,
  collectionId?: string | null
): T => {
  const ctx = useApiClientFeatureContext();
  const appMode = useSelector(getAppMode);
  const { apiClientWorkloadManager } = useApiClientContext();
  const autoGeneratedStore = useContext(AutogenerateStoreContext);
  const [getActiveEnvironment] = useAPIEnvironment((s) => [s.getActiveEnvironment]);
  const {
    env: { setEnvironmentVariables },
    api: { setCollectionVariables },
  } = useCommand();
  const { environmentVariablesRepository } = useApiClientRepository();

  const handleUpdatesFromExecutionWorker = useCallback(
    async (state: BaseExecutionContext) => {
      try {
        for (const key in state) {
          if (key === "environment") {
            const activeEnvironment = getActiveEnvironment();
            if (activeEnvironment) {
              await setEnvironmentVariables({ environmentId: activeEnvironment.id, variables: state[key] });
            }
          }
          if (key === "global") {
            const globalEnvId = environmentVariablesRepository.getGlobalEnvironmentId();
            await setEnvironmentVariables({ environmentId: globalEnvId, variables: state[key] });
          }
          if (key === "collectionVariables") {
            if (!collectionId) {
              continue;
            }

            await setCollectionVariables({
              collectionId,
              variables: state[key],
            });
          }
          if (key === "variables") {
            setRuntimeStore(state[key]);
          }
        }
      } catch (error) {
        console.log("Failed to update variables from script execution", error);
        toast.error("Failed to update variables from script execution");
      }
    },
    [
      collectionId,
      environmentVariablesRepository,
      getActiveEnvironment,
      setCollectionVariables,
      setEnvironmentVariables,
    ]
  );

  return useMemo(() => {
    const requestPreparer = new HttpRequestPreparationService(ctx, autoGeneratedStore, renderVariables);
    const requestValidator = new HttpRequestValidationService();

    return new ExecutorClass(
      ctx,
      requestPreparer,
      requestValidator,
      apiClientWorkloadManager,
      handleUpdatesFromExecutionWorker,
      appMode
    );
  }, [ExecutorClass, apiClientWorkloadManager, appMode, autoGeneratedStore, ctx, handleUpdatesFromExecutionWorker]);
};
